package connect4swt.gui;

import com.swtdesigner.*;
import game.Player;

import java.util.Collection;

import messages.InvalidInvitationReceivedMessage;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

import java.util.*;

import connect4swt.ClientApplication;
import connect4client.user.User;

/**
 * @author  Joel Cordeiro
 */
public class ClientWindow {

	private Label lblDebugStatus;
	private Text txtDebug;

	/**
	 * @return
	 * @uml.property  name="instance"
	 */
	public static ClientWindow getInstance() {
		if (instance == null) {
			synchronized (ClientWindow.class) {
				instance = new ClientWindow();
			}
		}
		
		return instance;
	}
	
	private ClientWindow() { }	
	
	public void open() {	

		new Thread(new Runnable() {
			public void run() {			
				display = Display.getDefault();

				display.syncExec(new Runnable() {
					public void run() {
						createContents();		

						shell.open();
						shell.layout();

						synchronized (openMutex) {
							openMutex.notify();
						}
						
						while (!shell.isDisposed()) {
							if (!display.readAndDispatch())
								display.sleep();
						}
					}
				});
			}
		}).start();
	}
	

	protected void createContents() {
		//Creating the shell window
		shell = new Shell(SWT.DIALOG_TRIM);
		shell.addShellListener(new ShellAdapter() {
			public void shellClosed(ShellEvent e) {
				if(showQuitBox()){
					ClientApplication.getController().getGUIMediator().sendQuitMessage();	
					//Cleaning resources cached by resource manager
					SWTResourceManager.dispose();
				} else {
					e.doit = false;
				}
			}
		});
		shell.setLayout(new FormLayout());
		shell.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/connect4Icon.ico"));
		shell.setSize(800, 600);
		shell.setText("Connect4                                                   by ExplicitSoft");


		//Creating the container for the tabs
		tabContainer = new TabFolder(shell, SWT.NONE);
		final FormData tabsFormLayoutData = new FormData();
		tabsFormLayoutData.height = 498;
		tabsFormLayoutData.left = new FormAttachment(0, 1);
		tabContainer.setLayoutData(tabsFormLayoutData);

		//Creating the first tab (game stats)
		final TabItem tabStats = new TabItem(tabContainer, SWT.NONE);
		tabStats.setText("Stats");

		final TabItem tiDebug = new TabItem(tabContainer, SWT.NONE);
		tiDebug.setText("Debug");

		final Composite cmpDebug = new Composite(tabContainer, SWT.NONE);
		cmpDebug.setLayout(new FormLayout());
		tiDebug.setControl(cmpDebug);

		txtDebug = new Text(cmpDebug, SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.WRAP);
		final FormData formData = new FormData();
		formData.bottom = new FormAttachment(0, 475);
		formData.right = new FormAttachment(100, -8);
		formData.top = new FormAttachment(0, 5);
		formData.left = new FormAttachment(0, 0);
		txtDebug.setLayoutData(formData);
		txtDebug.setEditable(false);
		txtDebug.setBackground(SWTResourceManager.getColor(255, 255, 255));

		lblDebugStatus = new Label(cmpDebug, SWT.NONE);
		final FormData formData_1 = new FormData();
		formData_1.top = new FormAttachment(0, 479);
		formData_1.right = new FormAttachment(0, 792);
		formData_1.left = new FormAttachment(0, 2);
		lblDebugStatus.setLayoutData(formData_1);
		lblDebugStatus.setText("Debug mode...");

		//Creating the composite that will contain the sash forms
		final Composite cmpStats = new Composite(tabContainer, SWT.NONE);
		tabStats.setControl(cmpStats);

		//Creating main sash form
		final SashForm sfMain = new SashForm(cmpStats, SWT.HORIZONTAL);
		sfMain.setBounds(0, 37, 786, 439);

		//Creating the first child sash form to hold the clients' list
		final SashForm sfClients = new SashForm(sfMain, SWT.VERTICAL);

		//Creating the second child sash form to hold the ranking list
		final SashForm sfRankingChat = new SashForm(sfClients, SWT.VERTICAL);

		//Creating the ranking list

		//Creating the "Chat:" label

		//Creating the chat window
		txtChat = new StyledText(sfRankingChat, SWT.BORDER | SWT.MULTI  | SWT.WRAP | SWT.V_SCROLL);
		txtChat.setBackground(SWTResourceManager.getColor(255, 255, 255));
		txtChat.setEditable(false);

		//Creating the "Send message" buttton

		//Creating the send message textbox
		txtSendMsg = new StyledText(sfRankingChat, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
		txtSendMsg.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				if(e.character == SWT.CR) {
					sendMessageToAll();
				}
			}
		});
		sfRankingChat.setWeights(new int[] {370, 77 });
		sfClients.setWeights(new int[] {379 });

		//Creating the clients tree list
		treeClients = new Tree(sfMain, SWT.BORDER);

		//Players' Popup Menu
		playersMenu = new Menu(treeClients);
		treeClients.setMenu(playersMenu);

		pmenuInvite = new MenuItem(playersMenu, SWT.NONE);
		pmenuInvite.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/newGame.png"));
		pmenuInvite.setText(" Invite...");
		pmenuInvite.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if(treeClients.getSelection().length > 0) {
					String playerName = ((Player)treeClients.getSelection()[0].getData()).getLogin();
					
					//Just simulationg invitation
					InviteOpponentWindow inviteOpponentWindow = new InviteOpponentWindow(shell,playerName);
					inviteOpponentWindow.open(ClientApplication.getController().getGameManager().getGames());	
				}
			}
		});

		pmenuInfo = new MenuItem(playersMenu, SWT.NONE);
		pmenuInfo.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/about.png"));
		pmenuInfo.setText(" Information...");
		pmenuInfo.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				// Para testes
				ClientApplication.getController().getGUIMediator().sendAccountEditMessage("pureza", "pass", "Admin");
				//TODO: Show information window...
			}
		});
		
		playersMenu.addMenuListener(new MenuListener() {

			public void menuHidden(MenuEvent e) { }

			public void menuShown(MenuEvent e) {
				if(treeClients.getSelection().length > 0) {
					String playerName = ((Player)treeClients.getSelection()[0].getData()).getLogin();
					
					if (playerName.equals(ClientApplication.getController().getUser().getLogin())) {
						pmenuInvite.setEnabled(false);
					} else {
						pmenuInvite.setEnabled(true);
					}
				}
			}
			
		});

		sfMain.setWeights(new int[] {641, 142 });

		//Creating the "Clients:" label
		final Label lblClients = new Label(cmpStats, SWT.NONE);
		lblClients.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/clientsHeader.png"));
		lblClients.setBounds(640, 10, 86, 25);

		//Creating the "Ranking:" label
		final Label lblRanking = new Label(cmpStats, SWT.NONE);
		lblRanking.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/chatHeader.png"));
		lblRanking.setBounds(1, 8, 86, 25);

		lblStatus = new Label(cmpStats, SWT.NONE);
		lblStatus.setText("Status messages...");
		lblStatus.setBounds(1, 480, 785, 13);

		ToolBar mainToolbar;
		mainToolbar = new ToolBar(shell, SWT.NONE);
		tabsFormLayoutData.right = new FormAttachment(mainToolbar, 0, SWT.RIGHT);
		tabsFormLayoutData.top = new FormAttachment(mainToolbar, 5, SWT.BOTTOM);
		mainToolbar.setBackgroundImage(SWTResourceManager.getImage(ClientWindow.class, "/images/toolbarHeader.png"));
		final FormData toolbarFormLayoutData = new FormData();
		toolbarFormLayoutData.bottom = new FormAttachment(0, 40);
		toolbarFormLayoutData.top = new FormAttachment(0, 0);
		toolbarFormLayoutData.right = new FormAttachment(0, 795);
		toolbarFormLayoutData.left = new FormAttachment(0, -1);
		mainToolbar.setLayoutData(toolbarFormLayoutData);

		final ToolItem tbiExit = new ToolItem(mainToolbar, SWT.PUSH);
		tbiExit.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent arg0) {	
				if(showQuitBox()){
					ClientApplication.getController().getGUIMediator().sendQuitMessage();
					//Cleaning resources cached by resource manager
					SWTResourceManager.dispose();
				}
			}
		});
		tbiExit.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/exit.png"));
		tbiExit.setText("Quit App");

		final ToolItem tbiSettings = new ToolItem(mainToolbar, SWT.PUSH);
		tbiSettings.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent arg0) {
				LoginWindow loginWindow = LoginWindow.getInstance();
				loginWindow.open();
			}
		});
		tbiSettings.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/setts.png"));
		tbiSettings.setText("Settings");

		final ToolItem tbiAbout = new ToolItem(mainToolbar, SWT.PUSH);
		tbiAbout.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/about.png"));
		tbiAbout.setText("About ");

		final ToolItem tbiHelp = new ToolItem(mainToolbar, SWT.PUSH);
		tbiHelp.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/help.png"));
		tbiHelp.setText("Help Me!");

		//Controller.getInstance().getConnectionController().sendListPlayersMessage();		
	}	

	
	
	public void receiveWallMessage(String login,String message){					
		String text = login + " says:\r\n" + message + "\r\n";
		int userTextLength = (login + " says:\r\n").length();
		int messageTextLength = (message + "\r\n").length();
				
		Color nickColor = new Color(getTxtChat().getDisplay(), 0, 0, 0 );
		
		StyleRange nickStyleRange =  new StyleRange(getTxtChat().getCharCount(),userTextLength,nickColor,null,SWT.BOLD);
		StyleRange textStyleRange =  new StyleRange(getTxtChat().getCharCount() + userTextLength,messageTextLength,nickColor,null,SWT.ITALIC);
			    	    
	    getTxtChat().append(text);
	    getTxtChat().setTopIndex(getTxtChat().getLineCount());

	   	getTxtChat().setStyleRange(nickStyleRange);
	    getTxtChat().setStyleRange(textStyleRange);	    
	}

	public void refreshRankings(Collection<Player> players){
		int i = 0;
		if (this.treeClients.getItemCount() > 0) {
			TreeItem[] items = this.treeClients.getItems();
			for(Player player : players){	
				if(((Player)items[i].getData()).equals(player)){
					String playerName = String.format("%-10s %d", player.getLogin(), player.getRanking());
					items[i].setText(playerName);    		 
				}	          
				i++;
			}
		}		
		
		User user = ClientApplication.getController().getUser();
		shell.setText("Explicitsoft Connect4             User: " + user.getLogin() + "      Ranking: " + user.getRanking());
	}

	public void receiveLoggedOutMessage(final String playerLogin){	

		if (playerLogin == null) {
			System.err.println("The server disconnected you. Maybe you logged in from somewhere else?");
			MessageBox msgBox = new MessageBox(this.shell, SWT.ICON_ERROR | SWT.OK);
			msgBox.setMessage("The server disconnected you. Maybe you logged in from somewhere else?");
			msgBox.open();
			System.exit(0);
		}
						
		String text = playerLogin + " logged out\n\n";
		
		StyleRange styleRange = new StyleRange();	    	
		styleRange.length = text.length();
		styleRange.fontStyle = SWT.BOLD;	    	
		styleRange.start = getTxtChat().getCharCount();
		styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);

		getTxtChat().append(text);
		getTxtChat().setTopIndex(getTxtChat().getLineCount());		
		getTxtChat().setStyleRange(styleRange);	  

		//Remove user from the clients tree
		if (this.treeClients.getItemCount() > 0) {
			TreeItem[] items = this.treeClients.getItems();
			for (int i = 0; i < items.length; i++) {	            
				if (((Player) items[i].getData()).getLogin().equals(playerLogin)){
					items[i].dispose();
					break;
				}	           
			}
		}			    		    	
	}

	/**
	 * If the user clicks the close button, a message box appears asking if the 
	 * user really wants to quit.
	 * @return
	 */
	public boolean showQuitBox(){		
		MessageBox msgBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.YES | SWT.NO);
		msgBox.setText("Warning");
		msgBox.setMessage("Do you really want to give up?");		
		if(msgBox.open() == SWT.YES) 
			return  true;			
		else
			return false;	
	}

	public void receiveLoggedInMessage(Player player){	
		String text = player.getLogin() + " logged in\n\n";

		StyleRange styleRange = new StyleRange();	    	
		styleRange.length = text.length();
		styleRange.fontStyle = SWT.BOLD;	    	
		styleRange.start = getTxtChat().getCharCount();;
		styleRange.foreground = display.getSystemColor(SWT.COLOR_BLUE);

		getTxtChat().append(text);
		getTxtChat().setTopIndex(getTxtChat().getLineCount());
		
		getTxtChat().setStyleRange(styleRange);	   
	}

	private void sendMessageToAll() {
		if(this.getTxtSendMsg().getText().length() != 0) {
			if(!(this.getTxtSendMsg().getText().trim().length() == 0)) {				
				ClientApplication.getController().getGUIMediator().sendWallMessage(this.getTxtSendMsg().getText());		
			}
			this.getTxtSendMsg().setText("");
		}
	}

	public void addPlayerToPlayersList(Player player) {						
		TreeItem[] items = this.treeClients.getItems();
		for(int i = 0;i < items.length;i++){	 	        	 
			if(((Player)items[i].getData()).equals(player)){
				return;  		 
			}		
		}		
		
		String playerName = String.format("%-10s %d", player.getLogin(), player.getRanking());
		TreeItem game = new TreeItem(treeClients, SWT.NONE);
		game.setImage(SWTResourceManager.getImage(ClientWindow.class, "/images/opponentIcon.png"));		
		game.setText(playerName);
		game.setData(player);
	}

	/**
	 * This method should be called when an InviteReplyMessage is received, and the isAccepted() method returns true.
	 * 
	 * @param playerName The current player's name.
	 * @param opponentName The opponent player's name.
	 * @param gameName The game's name.
	 * @param gameId The game's ID.
	 */
	public void opponentAcceptedInvitation(Player opponent, String gameName) {
		writeDebugMessage("Invitation accepted from " + opponent.getLogin() + " (" + gameName + ")");
//		MessageBox msgBox = new MessageBox(this.shell, SWT.ICON_INFORMATION | SWT.OK);
//		msgBox.setText("Invitation Accepted");
//		msgBox.setMessage("Invitation accepted from " + opponentName + " (" + gameName + ")");
//		msgBox.open();		
	}

	public GameTab matchStarted(Player me, Player opponent, String gameName, int matchId, String nextPlayer) {
		GameTab gameTab = new GameTab(tabContainer, me, opponent, gameName, matchId);
		if (nextPlayer.equals(me.getLogin()))
			gameTab.getGameBoard().setPlayerOneTurn(true);
		gameTab.getGameBoard().definePlayers();	
		gameTab.getGameBoard().createPieces();
			
			
		tabContainer.setSelection(tabContainer.getItemCount() - 1);
		return gameTab;
	}

	/**
	 * This method should be called when an InviteReplyMessage is received, and the isAccepted() method returns false.
	 * 
	 * @param playerName The current player's name.
	 * @param opponentName The opponent player's name.
	 * @param gameName The game's name.
	 * @param gameId The game's ID.
	 */
	public void opponentDeniedInvitation(Player opponent, String gameName) {
		writeDebugMessage("Invitation denied from " + opponent.getLogin() + " (" + gameName + ")");
		MessageBox msgBox = new MessageBox(this.shell, SWT.ICON_INFORMATION | SWT.OK);
		msgBox.setText("Invitation denied!");
		msgBox.setMessage(opponent.getLogin() + " denied your " + gameName + " game invitation!");
		msgBox.open();				
	}


	/**
	 * This method should be called when an InviteMessage is received.
	 * 
	 * @param playerName The current player's name.
	 * @param opponentName The opponent player's name.
	 * @param gameName The game's name.
	 * @param gameId The game's ID.
	 */
	public void invitationReceived(String opponentName, String gameName,int replyId) {
		writeDebugMessage("Invitation sent from " + opponentName + " (" + gameName + ")");
		MessageBox msgBox = new MessageBox(this.shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
		msgBox.setText("Invitation received!");
		msgBox.setMessage(opponentName + " has invited you to play \"" + gameName + "\".\nDo you wish to accept this invitation?");
		boolean accepted;

		if(msgBox.open() == SWT.YES) 
			accepted = true;			
		else
			accepted = false;

		ClientApplication.getController().getGUIMediator().sendInvitationReplyMessage(replyId,accepted);
	}

	/**
	 * This method should be called when an InvalidInvitationReceived is received.
	 * 
	 */
	public void invalidInvitationReceived(InvalidInvitationReceivedMessage.ErrorCode errorCode) {
		MessageBox msgBox = new MessageBox(this.shell, SWT.ICON_WARNING | SWT.OK);
		msgBox.setText("Invalid Invitation sent");	
		msgBox.open();
	}

	public void receiveInvitationTimedOut(String login, String game) {
		MessageBox msgBox = new MessageBox(this.shell, SWT.ICON_WARNING | SWT.OK);
		msgBox.setText("Invitation timed out!");	
		msgBox.setMessage("Invitation to player "+login+ " for game " + game + "failed!");
		msgBox.open();
	}

	public void writeDebugMessage(String message) {
		this.txtDebug.append("Message created at " + Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime() + "\r\n");
		this.txtDebug.append(" * " + message + "\r\n\r\n");
	}

	/**
	 * @return
	 * @uml.property  name="txtSendMsg"
	 */
	public StyledText getTxtSendMsg() {
		return txtSendMsg;
	}

	/**
	 * @return
	 * @uml.property  name="txtChat"
	 */
	public StyledText getTxtChat() {
		return txtChat;
	}

	public void setLblStatus(String text) {
		this.lblStatus.setText(text);
	}

	/**
	 * @return
	 * @uml.property  name="display"
	 */
	public Display getDisplay(){
		return display;
	}

	/**
	 * @return
	 * @uml.property  name="tabContainer"
	 */
	public TabFolder getTabContainer(){
		return this.tabContainer;
	}
	

	public void removePlayer(){

	}
	
	
	public Object openMutex = new Object();


	private StyledText txtSendMsg;
	private StyledText txtChat;
	private Tree treeClients;
	private Label lblStatus;
	private Menu playersMenu;
	private MenuItem pmenuInvite;
	private MenuItem pmenuInfo;
	protected Shell shell;
	private TabFolder tabContainer;
	private static Display display;
	private static ClientWindow instance;
}
